package com.xuzhiguang.baidu.pan.sdk.operations;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xuzhiguang.baidu.pan.sdk.common.http.*;
import com.xuzhiguang.baidu.pan.sdk.configuration.BaiduPanConfiguration;
import com.xuzhiguang.baidu.pan.sdk.exception.BaiduPanSdkException;
import com.xuzhiguang.baidu.pan.sdk.model.BaiduPanCommonResult;
import com.xuzhiguang.baidu.pan.sdk.token.AccessToken;
import com.xuzhiguang.baidu.pan.sdk.token.ITokenStorage;
import com.xuzhiguang.baidu.pan.sdk.util.BeanUtil;

import java.io.IOException;
import java.util.Map;

/**
 * @author xuzhiguang
 * @date 2020/6/25 17:06
 */
public class BaiduPanOperations {

    protected final String baiduPanApiUrlPrefix = "https://pan.baidu.com/rest/2.0";

    protected final HttpClient httpClient;

    protected final ITokenStorage tokenStorage;

    protected final BaiduPanConfiguration baiduPanConfiguration;

    protected final ObjectMapper objectMapper = new ObjectMapper();

    private final BaiduPanOAuthOperations baiduPanOAuthOperations;

    public BaiduPanOperations(HttpClient httpClient, ITokenStorage tokenStorage, BaiduPanConfiguration baiduPanConfiguration) {
        this.httpClient = httpClient;
        this.tokenStorage = tokenStorage;
        this.baiduPanConfiguration = baiduPanConfiguration;

        this.baiduPanOAuthOperations = new BaiduPanOAuthOperations(httpClient, tokenStorage, baiduPanConfiguration);
    }

    public <T extends BaiduPanCommonResult> T postMultipart(String url, Map<String, String> parameters,
                                                            Map<String, String> headers, Map<String, String> formDatas,
                                                            String fileKey, String fileName, byte[] file,
                                                            boolean needAuth, JavaType javaType) throws BaiduPanSdkException {
        MultipartBodyRequestBuilder builder = new MultipartBodyRequestBuilder()
                .url(url)
                .addParameters(parameters)
                .addHeaders(headers);

        if (formDatas != null) {
            formDatas.forEach(builder::addFormData);
        }

        builder.addFile(fileKey, fileName, file);

        Request request = builder.build();
        return doOperation(request, needAuth, javaType);
    }

    public <T extends BaiduPanCommonResult> T postFormData(String url, Map<String, String> parameters,
                                                           Map<String, String> headers, Map<String, String> formDatas,
                                                           boolean needAuth, JavaType javaType) throws BaiduPanSdkException {

        Request request = new FormBodyRequestBuilder()
                .url(url)
                .addParameters(parameters)
                .addHeaders(headers)
                .addFormDatas(formDatas)
                .build();

        return doOperation(request, needAuth, javaType);
    }

    public <T extends BaiduPanCommonResult> T get(String url, Map<String, String> parameters, Map<String, String> headers,
                             boolean needAuth, JavaType javaType) throws BaiduPanSdkException {

        return doOperation(url, HttpMethod.GET, parameters, headers, needAuth, javaType);
    }

    public <T extends BaiduPanCommonResult> T doOperation(String url, HttpMethod httpMethod, Map<String, String> parameters, Map<String, String> headers,
                             boolean needAuth, JavaType javaType) throws BaiduPanSdkException {
        Request request = new Request();
        request.setUrl(url);
        request.setHttpMethod(httpMethod);
        request.setParameters(parameters);
        request.setHeaders(headers);

        return this.doOperation(request, needAuth, javaType);
    }

    public <T extends BaiduPanCommonResult> T doOperation(Request request, boolean needAuth, JavaType javaType) throws BaiduPanSdkException {

        Response response = this.doOperation(request, needAuth);

        T t;
        try {
            t = objectMapper.readValue(response.getContent(), javaType);
        } catch (IOException e) {
            throw new BaiduPanSdkException("反序列化失败", e);
        }
        return t;
    }

    /**
     * 执行操作
     * @param request 请求数据
     * @param needAuth 是否需要accessToken验证
     * @return 返回数据
     */
    public Response doOperation(Request request, boolean needAuth) throws BaiduPanSdkException {

        if (needAuth) {
            request.getParameters().put("access_token", getAccessToken().getAccessToken());
        }

        return httpClient.execute(request);
    }

    protected Map<String, String> beanToMap(Object object) throws BaiduPanSdkException {
        try {
            return BeanUtil.beanToMap(object);
        } catch (JsonProcessingException e) {
            throw new BaiduPanSdkException("json 序列化/反序列化 失败", e);
        }
    }

    /**
     * 获取token
     * @return
     */
    private AccessToken getAccessToken() throws BaiduPanSdkException {
        return getAccessToken(true);
    }

    /**
     * 获取token
     * @param forceRefresh
     * @return
     */
    private AccessToken getAccessToken(boolean forceRefresh) throws BaiduPanSdkException {

        AccessToken accessToken = tokenStorage.get(baiduPanConfiguration.getApiKey());

        if (accessToken == null) {
            throw new BaiduPanSdkException("access_token不存在，请重新授权");
        }

        if (forceRefresh && accessToken.isExpired()) {
            refreshToken(accessToken.getRefreshToken());
            accessToken = tokenStorage.get(baiduPanConfiguration.getApiKey());
        }

        return accessToken;
    }

    /**
     * 刷新token
     * @param refreshToken
     * @return
     */
    private void refreshToken(String refreshToken) throws BaiduPanSdkException {
        baiduPanOAuthOperations.refreshToken(refreshToken);
    }

}
